/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#ifndef PLINTERRUPTSTATE_H
#define PLINTERRUPTSTATE_H

#include <stddef.h>
using namespace std;

/**
 * Interrupt State
 */
class InterruptState {
public:
	/**
	 * Get the stack-pointer before the interrupt occured
	 *
	 * @return the stack-pointer before the interrupt
	 */
	inline uintptr_t getStackPointer() const;
	/**
	 * Set the userspace stack-pointer
	 *
	 * @param stackPointer the new stack-pointer
	 */
	inline void setStackPointer(uintptr_t stackPointer);
	/**
	 * Get the instruction-pointer of the next instruction that is executed
	 * after the interrupt is processed
	 *
	 * @return the instruction-pointer
	 */
	inline uintptr_t getInstructionPointer() const;
	/**
	 * Set the instruction-pointer
	 *
	 * @param instructionPointer the new instruction-pointer
	 */
	inline void setInstructionPointer(uintptr_t instructionPointer);
	/**
	 * Get the base-pointer
	 *
	 * @return the base-pointer
	 */
	inline uintptr_t getBasePointer() const;
	/**
	 * Set the base-pointer
	 *
	 * @param basePointer the new base-pointer
	 */
	inline void setBasePointer(uintptr_t basePointer);
	/**
	 * Get the number of registers
	 *
	 * @return the number of registers
	 */
	inline size_t getRegisterCount() const;
	/**
	 * Get a specific register
	 *
	 * @param index the index of the register (from 0 to getRegisterCount() - 1)
	 * @return the value of the register
	 */
	processor_register_t getRegister(size_t index) const;
	/**
	 * Get the name of a specific register
	 *
	 * @param index the index of the register (from 0 to getRegisterCount() - 1)
	 * @return the name of the register
	 */
	const char *getRegisterName(size_t index) const;
	/**
	 * Get the register's size in bytes
	 *
	 * @param index the index of the register (from 0 to getRegisterCount() - 1)
	 * @return the register size in bytes
	 */
	inline size_t getRegisterSize(size_t index) const;
	/**
	 * Did the interrupt happen in kernel-mode?
	 *
	 * @return true, if the interrupt happened in kernel-mode, false otherwise
	 */
	inline bool kernelMode() const;
	/**
	 * Get the interrupt number
	 *
	 * @return the interrupt number
	 */
	inline size_t getInterruptNumber() const;
	/**
	 * Get the syscall service number
	 *
	 * @return the syscall service number
	 */
	inline size_t getSyscallService() const;
	/**
	 * Get the syscall function number
	 *
	 * @return the syscall function number
	 */
	inline size_t getSyscallNumber() const;
	/**
	 * Get the n'th parameter for this syscall.
	 *
	 * @param n The syscall number
	 * @return The syscall
	 */
	inline uintptr_t getSyscallParameter(size_t n) const;
	/**
	 * Set the syscall return value.
	 *
	 * @param val The syscall return value.
	 */
	inline void setSyscallReturnValue(uintptr_t val);
	/**
	 * Get the flags register
	 *
	 * @return the flags register
	 */
	inline uint32_t getFlags() const;
	/**
	 * Set the flags register
	 *
	 * @param flags the new flags
	 */
	inline void setFlags(uint32_t flags);
	/**
	 * The default constructor
	 *
	 * @note NOT implemented
	 */
	InterruptState() {}
	/**
	 * The copy-constructor
	 *
	 * @note NOT implemented
	 * @param The copy instance
	 */
	InterruptState(const InterruptState &);
	/**
	 * The assignement operator
	 *
	 * @note NOT implemented
	 * @param The copy instance
	 * @return this 
	 */
	InterruptState &operator = (const InterruptState &);
	/**
	 * The destructor
	 *
	 * @note NOT implemented
	 */
	~InterruptState() {}
	/**
	 * The DS segment register (zero-extended to 32bit)
	 */
	uint32_t m_Ds;
	/**
	 * The EDI general purpose register
	 */
	uint32_t m_Edi;
	/**
	 * The ESI general purpose register
	 */
	uint32_t m_Esi;
	/**
	 * The base-pointer register
	 */
	uint32_t m_Ebp;
	/**
	 * Reserved/Unused (ESP)
	 */
	uint32_t m_Res;
	/**
	 * The EBX general purpose register
	 */
	uint32_t m_Ebx;
	/**
	 * The EDX general purpose register
	 */
	uint32_t m_Edx;
	/**
	 * The ECX general purpose register
	 */
	uint32_t m_Ecx;
	/**
	 * The EAX general purpose register
	 */
	uint32_t m_Eax;
	/**
	 * The interrupt number
	 */
	uint32_t m_IntNumber;
	/**
	 * The error-code (if any)
	 */
	uint32_t m_Errorcode;
	/**
	 * The instruction pointer
	 */
	uint32_t m_Eip;
	/**
	 * The CS segment register (zero-extended to 32bit)
	 */
	uint32_t m_Cs;
	/**
	 * The extended flags (EFLAGS)
	 */
	uint32_t m_Eflags;
	/**
	 * The stack-pointer
	 */
	uint32_t m_Esp;
	/**
	 * The SS segment register
	 */
	uint32_t m_Ss;
} __attribute__((packed));

uintptr_t InterruptState::getStackPointer() const {
	if (kernelMode()) {
		return (m_Res + 20);
	}
	return m_Esp;
}

void InterruptState::setStackPointer(uintptr_t stackPointer) {
	if (!kernelMode()) {
		m_Esp = stackPointer;
	}
}

uintptr_t InterruptState::getInstructionPointer() const {
	return m_Eip;
}

void InterruptState::setInstructionPointer(uintptr_t instructionPointer) {
	m_Eip = instructionPointer;
}

uintptr_t InterruptState::getBasePointer() const {
	return m_Ebp;
}

void InterruptState::setBasePointer(uintptr_t basePointer) {
	m_Ebp = basePointer;
}

size_t InterruptState::getRegisterCount() const {
	return 15;
}

size_t InterruptState::getRegisterSize(size_t /*index*/) const {
	return 4;
}

bool InterruptState::kernelMode() const {
	return (m_Cs == 0x08);
}

size_t InterruptState::getInterruptNumber() const {
	return m_IntNumber;
}

size_t InterruptState::getSyscallService() const {
	return ((m_Eax >> 16) & 0xFFFF);
}

size_t InterruptState::getSyscallNumber() const {
	return (m_Eax & 0xFFFF);
}

void InterruptState::setSyscallReturnValue(uintptr_t val) {
	m_Eax = val;
}

uint32_t InterruptState::getFlags() const {
	return m_Eflags;
}

void InterruptState::setFlags(uint32_t flags) {
	m_Eflags = flags;
}

#endif
